//============================================================================
// Name        : csiena
// Author      : Josh Lospinoso
// Copyright   : MIT License
// Description : Austere, limited implementation of the R package RSiena in
//				 C++. Used for fast maximum likelihood and method of moments
//				 estimation of stochastic actor oriented models (SAOMs)
//				 without any frills or user experience niceties.
//============================================================================
// TODO: If there is a preprocessCUDA, it will be useful to have a digraphCUDA to store everything there.
#include "digraph.h"

namespace csiena {

Digraph::Digraph(int actors) {
	this->actors = actors;
	x = new bool[this->actors * this->actors];
	for (int i=0; i<actors; i++) {
		for (int j=0; j<actors; j++) {
			x[i * this->actors + j] = false;
		}
	}
}

Digraph::Digraph(const Digraph &donorDigraph) {
	this->actors = donorDigraph.actors;
	this->x = new bool[this->actors * this->actors];

}

Digraph::~Digraph() {
	delete(x);
}

void Digraph::copy(const csiena::Digraph &donor) {
	for (int i=0; i<actors; i++) {
		for (int j=0; j<actors; j++) {
			this->x[i * this->actors + j] = donor.x[i * this->actors + j];
		}
	}
}

int Digraph::getActors(){
	return this->actors;
}

bool Digraph::get(int i, int j) {
	if ((i < this->actors) & (j < this->actors)) {
		return x[i * this->actors + j];
	} else {
		std::cerr << "Digraph::get: i,j out of range " << i << "," << j << std::endl;
		return false;
	}
}

bool Digraph::set(int i, int j, bool xij) {
	if ((i < this->actors) & (j < this->actors)) {
		return x[i * this->actors + j] = xij;
	} else {
		std::cerr << "Digraph::set: i,j out of range " << i << "," << j << std::endl;
		return false;
	}
}

bool Digraph::flip(int i, int j) {
	if ((i < this->actors) & (j < this->actors)) {
		if (i != j) {
			if (this->get(i,j)) {
				x[i * this->actors + j] = false;
			} else {
				x[i * this->actors + j] = true;
			}
		}
		return x[i * this->actors + j];
	} else {
		std::cerr << "Digraph::flip: i,j out of range " << i << "," << j << std::endl;
		return false;
	}
}

void Digraph::print() {
	for (int i=0; i < this->actors; i++) {
		printf("%3.0u ",i);
		for (int j=0; j < this->actors; j++) {
			if (i==j) {
				std::cout << " ";
			} else if (this->get(i,j)) {
				std::cout << "1";
			} else {
				std::cout << "0";
			}
		}
		std::cout << std::endl;
	}
}


// Writes to a csv the network variable
void Digraph::writeNet(const char* fname) {
	this->fixDiag();
	std::ofstream fout(fname);
	for (int i=0; i<this->actors; i++) {
		for (int j=0; j<this->actors-1; j++) {
			if (this->x[i * this->actors + j]) {
				fout << "1,";
			} else {
				fout << "0,";
			}
		}
		if (i < this->actors-1) {
			if (this->x[(i+1) * this->actors -1]) {
				fout << "1\n";
			} else {
				fout << "0\n";
			}
		}
	}
	fout << "0";
	fout.close();
}


// Reads a csv for the state of the network variable
void Digraph::readNet(const char* fname) {
	std::ifstream file (fname);
	std::string lineStr;
	std::string valStr;
	int i=0;
	int j=0;
	char * pch;
	if (file.good()) {
		while (getline ( file, lineStr, '\n' )){
			pch = strtok(const_cast<char *>(lineStr.c_str()),",");
			while (pch != NULL) {
				if (atoi(pch) == 1) {
					this->x[i * this->actors + j] = true;
				} else {
					this->x[i * this->actors + j] = false;
				}
				pch = strtok(NULL, ",");
				j++;
			}
			j=0;
			i++;
		}
	} else {
		std::cerr << "Cannot open " << fname << " for writing network." << std::endl;
	}
	fixDiag();
	file.close();
}

// Copy the current state to the pointer given as an argument
void Digraph::xCopy(bool *copy) {
	this->fixDiag();
	for (int i=0; i<this->actors * this->actors; i++) {
		if(this->x[i]) {
			copy[i] = true;
		} else {
			copy[i] = false;
		}
	}
}

void Digraph::fixDiag() {
	for (int i=0; i<this->actors; i++) {
		this->x[(1+this->actors) * i ] = false;
	}
}

}
